/*
File: dbms.h
Authors: Team 3- Sam Stewart, Dylan McDougall, Stephen Ten Eyck
Date: 06/06/12
CSCE 315- 100 Summer 1 2012
*/

#ifndef DBMS_H_
#define DBMS_H_

/*
Class for the database management system
*/

#include "table.h"

/*==========================================================================*/
/*									DBMS CLASS									*/
/*==========================================================================*/

class DBMS{
	//private members
	vector<Table*> tables;		//list of tables in the database
	ifstream dbFile;			//filestream
	
public:
	//constructor
	DBMS();

	//public functions
	void clearTables();
	Table *getTable(string name);								//gets table corresponding to the given table name
	void execute(vector<string> tokens);						//executes tokens passed from parser
	Table select(Table *t, vector<string> cond);				//relational algebra: select
	Table project(Table *t, vector<string> attrs);				//relational algebra: project
	Table crossProduct(Table *t1, Table *t2);					//relational algebra: cross product (*)
	void createTable(string name_, vector<string> attrList_, vector<int> types_, vector<string> key_);		//create and add table to tables vector
	void addTable(Table t);										//adds table to tables vector
	void deleteTuple(Table *t, vector<string> deleteKey);
	void updateAttr(Table *t, vector<string> key, string attrOld, string attrNew);
	void addTuple(Table *t, vector<string> tuple);
	bool isTable(string tableName);								//checks whether the table given by the name exists in the tables vector
	void open(string fileName);
	void write(Table *t);
	void read(string fileName);
	void close();
	void show(Table *t);
	void exit();
	Table Query(vector<string> tokens);							//used in execute to solve expressions
};

//Default constructor
DBMS::DBMS(){

}

void DBMS::clearTables(){
	for(int i=0; i<tables.size(); i++){
		delete tables[i];
	}
	tables.clear();
}

Table *DBMS::getTable(string name){
	for(int i=0; i<tables.size(); i++){
		if(tables[i]->getName() == name){
			return tables[i];
		}
	}
	error("Table does not exist");
}

/*
This function takes a parsed vector of strings
and carries out the request.
*/
void DBMS::execute(vector<string> tokens){
	if(tokens.size() == 0){
		error("Execute: Nothing in token stream.");
	}
	/*===========================OPEN============================*/
	if(tokens[0] == "OPEN"){
		cout << "Opening table\n";
		string fileName = tokens[1];
		fileName += ".db";
		open(fileName);
	}
	/*===========================CLOSE============================*/
	else if(tokens[0] == "CLOSE"){
		cout << "Closing File\n";
		close();
	}
	/*===========================WRITE============================*/
	else if(tokens[0] == "WRITE"){
		cout << "Writing File\n";
		write(getTable(tokens[1]));
	}
	/*===========================EXIT============================*/
	else if(tokens[0] == "EXIT"){
		cout << "Exiting\n";
	}
	/*===========================SHOW============================*/
	else if(tokens[0] == "SHOW"){
		cout << "Showing table\n";
		tokens.erase(tokens.begin());
		Table *t = new Table(Query(tokens));
		show(t);
	}
	/*===========================CREATE============================*/
	else if(tokens[0] == "CREATE"){
		cout << "Creating table\n";
		string tempName = tokens[2];		//name of table
		int i = 4;							//4th token starts typed attr list
		vector<string> tempAttrList;
		vector<int> tempTypes;
		int pCount = 1;						//keeps track of parenthesis
		while(pCount != 0){					//exit when last parenthesis found
			if(tokens[i] == "("){
				pCount++;
			}
			else if(tokens[i] == ")"){
				pCount--;
			}
			else if(tokens[i] == "VARCHAR"){
				tempTypes.push_back(STRING);
			}
			else if(tokens[i] == "INTEGER"){
				tempTypes.push_back(INTEGER);
			}
			else if(tokens[i] == ","){
				;								//nop
			}
			else if(integerCheck(tokens[i])){
				;								//dont do anything with ints
			}
			else{
				tempAttrList.push_back(tokens[i]);
			}
			i++;								//next token
		}
		i += 3;									//skip next 3 tokens ("PRIMARY", "KEY", and "(")
		vector<string> tempKey;
		while(tokens[i] != ")"){				//found last key
			if(tokens[i] != ","){				//ignore commas
				tempKey.push_back(tokens[i]);
			}
			i++;								//next token
		}
		createTable(tempName, tempAttrList, tempTypes, tempKey);
	}
	/*===========================UPDATE============================*/
	else if(tokens[0] == "UPDATE"){
		cout << "Updating table\n";
		//see grammar rules for corresponding token possibilities
		string tempName = tokens[1];
		string tempAttrName = tokens[3];
		string tempAttrNew = tokens[5];
		vector<string> cond;
		//tokens 8, 9, and 10 are condition variables
		cond.push_back(tokens[8]);
		cond.push_back(tokens[9]);
		cond.push_back(tokens[10]);
		
		//remove quotes if present
		for(int i=0; i<cond.size(); i++){
			if(cond[i].at(0) == '\"'){
				cond[i].erase(cond[i].begin());
				cond[i].erase(cond[i].end()-1);
			}
		}
		
		//remove quotes from new attr
		if(!integerCheck(tempAttrNew)){
			tempAttrNew.erase(tempAttrNew.begin());
			tempAttrNew.erase(tempAttrNew.end()-1);
		}
		
		//keep track of relation name or literal types in cond
		bool firstOperandIsRelationName = false;
		bool secondOperandIsRelationName = false;
		int attrNum;		//number corresponding to attr in cond
		int attrNum1;		//only used if both operands are attrs
		Table *t;			//temporary table
		
		//get table from tables vector
		for(int i=0; i<tables.size(); i++){
			if(tables[i]->getName() == tempName){
				t = tables[i];
			}
		}
		
		int attrOldNum;		//number corresponding to old attr
		for(int i=0; i<t->getAttrList().size(); i++){
			if(tokens[3] == t->getAttrList()[i]){
				attrOldNum = i;
			}			
		}
		
		int temp1, temp2;	//converted strings to integers if integer types
		vector<string> tempKey;
		
		//test operands to check if they are literals or relation names
		for(int i=0; i<t->getAttrList().size(); i++){
			if(cond[0] == t->getAttrList()[i]){
				firstOperandIsRelationName = true;
				attrNum = i;
				attrNum1 = i;
			}
			if(cond[2] == t->getAttrList()[i]){
				secondOperandIsRelationName = true;
				attrNum = i;
			}
		}
		
		if(!secondOperandIsRelationName && !firstOperandIsRelationName){
			error("select: Both operands can't be literals");
		}
		
		if(secondOperandIsRelationName && firstOperandIsRelationName){	//both operands are relation names
			for(int i=0; i<t->numTuples(); i++){						//for each tuple
				if(cond[1] == "=="){
					if(t->getValue(i,attrNum1) == t->getValue(i,attrNum)){
						updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);	//update if condition is true for this tuple
					}
				}
				else if(cond[1] == "!="){
					if(t->getValue(i,attrNum1) != t->getValue(i,attrNum)){
						updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
					}
				}
				else if(cond[1] == "<"){
					if(t->getTypes()[attrNum1] == INTEGER && t->getTypes()[attrNum] == INTEGER){
						temp1 = atoi((t->getValue(i,attrNum1)).c_str());					//convert string to int
						temp2 = atoi((t->getValue(i,attrNum)).c_str());
						if(temp1 < temp2){
							updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
						}
					}
					else{//not integer
						if(t->getValue(i,attrNum1) < t->getValue(i,attrNum)){
							updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
						}
					}
				}
				else if(cond[1] == ">"){
					if(t->getTypes()[attrNum1] == INTEGER && t->getTypes()[attrNum] == INTEGER){
						temp1 = atoi((t->getValue(i,attrNum1)).c_str());
						temp2 = atoi((t->getValue(i,attrNum)).c_str());
						if(temp1 > temp2){
							updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
						}
					}
					else{//not integer
						if(t->getValue(i,attrNum1) > t->getValue(i,attrNum)){
							updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
						}
					}
				}
				else if(cond[1] == "<="){
					if(t->getTypes()[attrNum1] == INTEGER && t->getTypes()[attrNum] == INTEGER){
						temp1 = atoi((t->getValue(i,attrNum1)).c_str());
						temp2 = atoi((t->getValue(i,attrNum)).c_str());
						if(temp1 <= temp2){
							updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
						}
					}
					else{//not integer
						if(t->getValue(i,attrNum1) <= t->getValue(i,attrNum)){
							updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
						}
					}
				}
				else if(cond[1] == ">="){
					if(t->getTypes()[attrNum1] == INTEGER && t->getTypes()[attrNum] == INTEGER){
						temp1 = atoi((t->getValue(i,attrNum1)).c_str());
						temp2 = atoi((t->getValue(i,attrNum)).c_str());
						if(temp1 >= temp2){
							updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
						}
					}
					else{//not integer
						if(t->getValue(i,attrNum1) >= t->getValue(i,attrNum)){
							updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
						}
					}
				}
				else{
						error("select: Bad operator.");
				}
			}
		}
		else{//same thing for one relation name and one literal
			for(int i=0; i<t->numTuples(); i++){
				tempKey = t->getKeyFromTuple(i);
				if(cond[1] == "=="){
					if(firstOperandIsRelationName){//check if relation name is first or second operand
						if(t->getValue(i,attrNum) == cond[2]){
							updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
						}
					}
					else{
						if(t->getValue(i,attrNum) == cond[0]){
							updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
						}
					}
				}
				else if(cond[1] == "!="){
					if(firstOperandIsRelationName){
						if(t->getValue(i,attrNum) != cond[2]){
							updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
						}
					}
					else{
						if(t->getValue(i,attrNum) != cond[0]){
							updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
						}
					}
				}
				else if(cond[1] == "<"){
					if(t->getTypes()[attrNum] == INTEGER){
						if(firstOperandIsRelationName){
							temp1 = atoi((t->getValue(i,attrNum)).c_str());
							temp2 = atoi((cond[2]).c_str());
							if(temp1 < temp2){
								updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
							}
						}
						else{
							temp1 = atoi((cond[0]).c_str());
							temp2 = atoi((t->getValue(i,attrNum)).c_str()); 
							if(temp1 < temp2){
								updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
							}
						}
					}
					else{//not integer			
						if(firstOperandIsRelationName){
							if(t->getValue(i,attrNum) < cond[2]){
								updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
							}
						}
						else{
							if(t->getValue(i,attrNum) < cond[0]){
								updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
							}
						}
					}
				}
				else if(cond[1] == ">"){
					if(t->getTypes()[attrNum] == INTEGER){
						if(firstOperandIsRelationName){
							temp1 = atoi((t->getValue(i,attrNum)).c_str());
							temp2 = atoi((cond[2]).c_str());
							if(temp1 > temp2){
								updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
							}
						}
						else{
							temp1 = atoi((cond[0]).c_str());
							temp2 = atoi((t->getValue(i,attrNum)).c_str()); 
							if(temp1 > temp2){
								updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
							}
						}
					}
					else{//not integer			
						if(firstOperandIsRelationName){
							if(t->getValue(i,attrNum) > cond[2]){
								updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
							}
						}
						else{
							if(t->getValue(i,attrNum) > cond[0]){
								updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
							}
						}
					}
				}
				else if(cond[1] == "<="){
					if(t->getTypes()[attrNum] == INTEGER){
						if(firstOperandIsRelationName){
							temp1 = atoi((t->getValue(i,attrNum)).c_str());
							temp2 = atoi((cond[2]).c_str());
							if(temp1 <= temp2){
								updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
							}
						}
						else{
							temp1 = atoi((cond[0]).c_str());
							temp2 = atoi((t->getValue(i,attrNum)).c_str()); 
							if(temp1 <= temp2){
								updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
							}
						}
					}
					else{//not integer			
						if(firstOperandIsRelationName){
							if(t->getValue(i,attrNum) <= cond[2]){
								updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
							}
						}
						else{
							if(t->getValue(i,attrNum) <= cond[0]){
								updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
							}
						}
					}
				}
				else if(cond[1] == ">="){
					if(t->getTypes()[attrNum] == INTEGER){
						if(firstOperandIsRelationName){
							temp1 = atoi((t->getValue(i,attrNum)).c_str());
							temp2 = atoi((cond[2]).c_str());
							if(temp1 >= temp2){
								updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
							}
						}
						else{
							temp1 = atoi((cond[0]).c_str());
							temp2 = atoi((t->getValue(i,attrNum)).c_str()); 
							if(temp1 >= temp2){
								updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
							}
						}
					}
					else{//not integer			
						if(firstOperandIsRelationName){
							if(t->getValue(i,attrNum) >= cond[2]){
								updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
							}
						}
						else{
							if(t->getValue(i,attrNum) >= cond[0]){
								updateAttr(t, tempKey, t->getValue(i, attrOldNum), tempAttrNew);
							}
						}
					}
				}
				else{
					error("select: Bad operator.");
				}
			}
		}
	}
	/*===========================INSERT============================*/
	else if(tokens[0] == "INSERT"){
		cout << "Inserting tuple\n";
		string tempName = tokens[2];
		Table *t;
		for(int i=0; i<tables.size(); i++){
			if(tables[i]->getName() == tempName){
				t = tables[i];
			}
		}
		vector<string> tempTuple;
		if(tokens[5] == "RELATION"){			//VALUES FROM RELATION case
			//delete first 6 tokens
			tokens.erase(tokens.begin());
			tokens.erase(tokens.begin());
			tokens.erase(tokens.begin());
			tokens.erase(tokens.begin());
			tokens.erase(tokens.begin());
			tokens.erase(tokens.begin());
			Table temp = Query(tokens);		//temp table from Query function
			//add tuples
			for(int i=0; i<temp.numTuples(); i++){
				addTuple(t, temp.getTuple(i));
			}
		}
		else{								//VALUES FROM !RELATION case
			int i = 6;						//token[6] = first literal
			while(tokens[i] != ")"){		//last literal found
				if(tokens[i] != ","){		//ignore commas
					if(integerCheck(tokens[i])){
						tempTuple.push_back(tokens[i]);		//token is integer
					}
					else{									//token is not integer
						//get rid of quotes
						string tempString = tokens[i];
						tempString.erase(tempString.begin());
						tempString.erase(tempString.end()-1);
						tempTuple.push_back(tempString);
					}
				}
				i++;
			}
			addTuple(t, tempTuple);
		}
	}
	/*===========================DELETE============================*/
	else if(tokens[0] == "DELETE"){
		//see grammar rules for corresponding tokens
		cout << "Deleting tuple\n";
		string tempName = tokens[2];
		vector<string> cond;
		cond.push_back(tokens[5]);
		cond.push_back(tokens[6]);
		cond.push_back(tokens[7]);
		Table *t;
		int temp1, temp2;		//temp ints used if operands are integers
		vector<string> tempKey;
		//test conditions like in INSERT case
		bool firstOperandIsRelationName = false;
		bool secondOperandIsRelationName = false;
		int attrNum, attrNum1;
		for(int i=0; i<tables.size(); i++){
			if(tables[i]->getName() == tempName){
				t = tables[i];
			}
		}
		
		for(int i=0; i<t->getAttrList().size(); i++){
			if(cond[0] == t->getAttrList()[i]){
				firstOperandIsRelationName = true;
				attrNum = i;
				attrNum1 = i;
			}
			if(cond[2] == t->getAttrList()[i]){
				secondOperandIsRelationName = true;
				attrNum = i;
			}
		}
		if(!secondOperandIsRelationName && !firstOperandIsRelationName){
			error("select: Both operands can't be literals");
		}
		if(secondOperandIsRelationName && firstOperandIsRelationName){
			for(int i=0; i<t->numTuples(); i++){						//for each tuple
				tempKey = t->getKeyFromTuple(i);
				if(cond[1] == "=="){
					if(t->getValue(i,attrNum1) == t->getValue(i,attrNum)){
						deleteTuple(t, tempKey);	//delete if condition is true for this tuple
					}
				}
				else if(cond[1] == "!="){
					if(t->getValue(i,attrNum1) != t->getValue(i,attrNum)){
						deleteTuple(t, tempKey);
					}
				}
				else if(cond[1] == "<"){
					if(t->getTypes()[attrNum1] == INTEGER && t->getTypes()[attrNum] == INTEGER){
						temp1 = atoi((t->getValue(i,attrNum1)).c_str());					//convert string to int
						temp2 = atoi((t->getValue(i,attrNum)).c_str());
						if(temp1 < temp2){
							deleteTuple(t, tempKey);
						}
					}
					else{//not integer
						if(t->getValue(i,attrNum1) < t->getValue(i,attrNum)){
							deleteTuple(t, tempKey);
						}
					}
				}
				else if(cond[1] == ">"){
					if(t->getTypes()[attrNum1] == INTEGER && t->getTypes()[attrNum] == INTEGER){
						temp1 = atoi((t->getValue(i,attrNum1)).c_str());
						temp2 = atoi((t->getValue(i,attrNum)).c_str());
						if(temp1 > temp2){
							deleteTuple(t, tempKey);
						}
					}
					else{//not integer
						if(t->getValue(i,attrNum1) > t->getValue(i,attrNum)){
							deleteTuple(t, tempKey);
						}
					}
				}
				else if(cond[1] == "<="){
					if(t->getTypes()[attrNum1] == INTEGER && t->getTypes()[attrNum] == INTEGER){
						temp1 = atoi((t->getValue(i,attrNum1)).c_str());
						temp2 = atoi((t->getValue(i,attrNum)).c_str());
						if(temp1 <= temp2){
							deleteTuple(t, tempKey);
						}
					}
					else{//not integer
						if(t->getValue(i,attrNum1) <= t->getValue(i,attrNum)){
							deleteTuple(t, tempKey);
						}
					}
				}
				else if(cond[1] == ">="){
					if(t->getTypes()[attrNum1] == INTEGER && t->getTypes()[attrNum] == INTEGER){
						temp1 = atoi((t->getValue(i,attrNum1)).c_str());
						temp2 = atoi((t->getValue(i,attrNum)).c_str());
						if(temp1 >= temp2){
							deleteTuple(t, tempKey);
						}
					}
					else{//not integer
						if(t->getValue(i,attrNum1) >= t->getValue(i,attrNum)){
							deleteTuple(t, tempKey);
						}
					}
				}
				else{
						error("select: Bad operator.");
				}
			}
		}
		else{//one literal and one relation name for operands
			for(int i=0; i<t->numTuples(); i++){
				tempKey = t->getKeyFromTuple(i);
				if(cond[1] == "=="){
					if(firstOperandIsRelationName){
						if(t->getValue(i,attrNum) == cond[2]){
							deleteTuple(t, tempKey);
						}
					}
					else{
						if(t->getValue(i,attrNum) == cond[0]){
							deleteTuple(t, tempKey);
						}
					}
				}
				else if(cond[1] == "!="){
					if(firstOperandIsRelationName){
						if(t->getValue(i,attrNum) != cond[2]){
							deleteTuple(t, tempKey);
						}
					}
					else{
						if(t->getValue(i,attrNum) != cond[0]){
							deleteTuple(t, tempKey);
						}
					}
				}
				else if(cond[1] == "<"){
					if(t->getTypes()[attrNum] == INTEGER){
						if(firstOperandIsRelationName){
							temp1 = atoi((t->getValue(i,attrNum)).c_str());
							temp2 = atoi((cond[2]).c_str());
							if(temp1 < temp2){
								deleteTuple(t, tempKey);
							}
						}
						else{
							temp1 = atoi((cond[0]).c_str());
							temp2 = atoi((t->getValue(i,attrNum)).c_str()); 
							if(temp1 < temp2){
								deleteTuple(t, tempKey);
							}
						}
					}
					else{//not integer			
						if(firstOperandIsRelationName){
							if(t->getValue(i,attrNum) < cond[2]){
								deleteTuple(t, tempKey);
							}
						}
						else{
							if(t->getValue(i,attrNum) < cond[0]){
								deleteTuple(t, tempKey);
							}
						}
					}
				}
				else if(cond[1] == ">"){
					if(t->getTypes()[attrNum] == INTEGER){
						if(firstOperandIsRelationName){
							temp1 = atoi((t->getValue(i,attrNum)).c_str());
							temp2 = atoi((cond[2]).c_str());
							if(temp1 > temp2){
								deleteTuple(t, tempKey);
							}
						}
						else{
							temp1 = atoi((cond[0]).c_str());
							temp2 = atoi((t->getValue(i,attrNum)).c_str()); 
							if(temp1 > temp2){
								deleteTuple(t, tempKey);
							}
						}
					}
					else{//not integer			
						if(firstOperandIsRelationName){
							if(t->getValue(i,attrNum) > cond[2]){
								deleteTuple(t, tempKey);
							}
						}
						else{
							if(t->getValue(i,attrNum) > cond[0]){
								deleteTuple(t, tempKey);
							}
						}
					}
				}
				else if(cond[1] == "<="){
					if(t->getTypes()[attrNum] == INTEGER){
						if(firstOperandIsRelationName){
							temp1 = atoi((t->getValue(i,attrNum)).c_str());
							temp2 = atoi((cond[2]).c_str());
							if(temp1 <= temp2){
								deleteTuple(t, tempKey);
							}
						}
						else{
							temp1 = atoi((cond[0]).c_str());
							temp2 = atoi((t->getValue(i,attrNum)).c_str()); 
							if(temp1 <= temp2){
								deleteTuple(t, tempKey);
							}
						}
					}
					else{//not integer			
						if(firstOperandIsRelationName){
							if(t->getValue(i,attrNum) <= cond[2]){
								deleteTuple(t, tempKey);
							}
						}
						else{
							if(t->getValue(i,attrNum) <= cond[0]){
								deleteTuple(t, tempKey);
							}
						}
					}
				}
				else if(cond[1] == ">="){
					if(t->getTypes()[attrNum] == INTEGER){
						if(firstOperandIsRelationName){
							temp1 = atoi((t->getValue(i,attrNum)).c_str());
							temp2 = atoi((cond[2]).c_str());
							if(temp1 >= temp2){
								deleteTuple(t, tempKey);
							}
						}
						else{
							temp1 = atoi((cond[0]).c_str());
							temp2 = atoi((t->getValue(i,attrNum)).c_str()); 
							if(temp1 >= temp2){
								deleteTuple(t, tempKey);
							}
						}
					}
					else{//not integer			
						if(firstOperandIsRelationName){
							if(t->getValue(i,attrNum) >= cond[2]){
								deleteTuple(t, tempKey);
							}
						}
						else{
							if(t->getValue(i,attrNum) >= cond[0]){
								deleteTuple(t, tempKey);
							}
						}
					}
				}
				else{
					error("select: Bad operator.");
				}
			}
		}
	}
	/*===========================QUERY============================*/
	else{
		cout << "Query\n";
		string newName = tokens[0];	//relation name
		//delete relation name and "<-"
		tokens.erase(tokens.begin());
		tokens.erase(tokens.begin());
		Table *t = new Table(Query(tokens));	//get table from Query function
		t->updateName(newName);					//change name to user request
		tables.push_back(t);					//add to tables vector
	}
}

Table DBMS::select(Table *t, vector<string> cond){
//condition must have 2 operands and 1 operator
	if(cond.size() != 3){
		error("Select: Bad condition.");
	}
	
	//get rid of quotes
	if(cond[0].at(0) == '\"'){
		cond[0].erase(cond[0].begin());
		cond[0].erase(cond[0].end()-1);
	}
	if(cond[2].at(0) == '\"'){
		cond[2].erase(cond[2].begin());
		cond[2].erase(cond[2].end()-1);
	}
	
	int temp1, temp2;							//converted strings to integers if integer types
	
	//keep track of whether each operand is a relation name or literal
	bool firstOperandIsRelationName = false;
	bool secondOperandIsRelationName = false;
	int attrNum, attrNum1;							//number corresponding to attr in cond
	
	//check whether each operand is a relation name or literal
	for(int i=0; i<t->getAttrList().size(); i++){
		if(cond[0] == t->getAttrList()[i]){
			firstOperandIsRelationName = true;
			attrNum = i;
			attrNum1 = i;							//only used if both operands are relation names
		}
		if(cond[2] == t->getAttrList()[i]){
			secondOperandIsRelationName = true;
			attrNum = i;
		}
	}
	
	//create temp table to be returned
	string tempName = t->getName() + " Selection";
	vector<string> tempAttrList = t->getAttrList();
	vector<int> tempTypes = t->getTypes();
	vector<string> tempKey = t->getKey();
	Table tempTable(tempName, tempAttrList, tempTypes, tempKey);
	vector<string> tempTuple;
	
	if(!secondOperandIsRelationName && !firstOperandIsRelationName){
		error("select: Both operands can't be literals");
	}
	
	if(secondOperandIsRelationName && firstOperandIsRelationName){//both operands are relation names
		for(int i=0; i<t->numTuples(); i++){//need to check condition for each tuple
			if(cond[1] == "=="){
				if(t->getValue(i,attrNum1) == t->getValue(i,attrNum)){
					tempTuple = t->getTuple(i);						//create and add tuple
					tempTable.addTuple(tempTuple);
				}
			}
			else if(cond[1] == "!="){
				if(t->getValue(i,attrNum1) != t->getValue(i,attrNum)){
					tempTuple = t->getTuple(i);
					tempTable.addTuple(tempTuple);
				}
			}
			else if(cond[1] == "<"){
				if(t->getTypes()[attrNum1] == INTEGER && t->getTypes()[attrNum] == INTEGER){
					temp1 = atoi((t->getValue(i,attrNum1)).c_str());
					temp2 = atoi((t->getValue(i,attrNum)).c_str());
					if(temp1 < temp2){
						tempTuple = t->getTuple(i);
						tempTable.addTuple(tempTuple);
					}
				}
				else{//not integer
					if(t->getValue(i,attrNum1) < t->getValue(i,attrNum)){
						tempTuple = t->getTuple(i);
						tempTable.addTuple(tempTuple);
					}
				}
			}
			else if(cond[1] == ">"){
				if(t->getTypes()[attrNum1] == INTEGER && t->getTypes()[attrNum] == INTEGER){
					temp1 = atoi((t->getValue(i,attrNum1)).c_str());
					temp2 = atoi((t->getValue(i,attrNum)).c_str());
					if(temp1 > temp2){
						tempTuple = t->getTuple(i);
						tempTable.addTuple(tempTuple);
					}
				}
				else{//not integer
					if(t->getValue(i,attrNum1) > t->getValue(i,attrNum)){
						tempTuple = t->getTuple(i);
						tempTable.addTuple(tempTuple);
					}
				}
			}
			else if(cond[1] == "<="){
				if(t->getTypes()[attrNum1] == INTEGER && t->getTypes()[attrNum] == INTEGER){
					temp1 = atoi((t->getValue(i,attrNum1)).c_str());
					temp2 = atoi((t->getValue(i,attrNum)).c_str());
					if(temp1 <= temp2){
						tempTuple = t->getTuple(i);
						tempTable.addTuple(tempTuple);
					}
				}
				else{//not integer
					if(t->getValue(i,attrNum1) <= t->getValue(i,attrNum)){
						tempTuple = t->getTuple(i);
						tempTable.addTuple(tempTuple);
					}
				}
			}
			else if(cond[1] == ">="){
				if(t->getTypes()[attrNum1] == INTEGER && t->getTypes()[attrNum] == INTEGER){
					temp1 = atoi((t->getValue(i,attrNum1)).c_str());
					temp2 = atoi((t->getValue(i,attrNum)).c_str());
					if(temp1 >= temp2){
						tempTuple = t->getTuple(i);
						tempTable.addTuple(tempTuple);
					}
				}
				else{//not integer
					if(t->getValue(i,attrNum1) >= t->getValue(i,attrNum)){
						tempTuple = t->getTuple(i);
						tempTable.addTuple(tempTuple);
					}
				}
			}
			else{
					error("select: Bad operator.");
			}
		}
	}
	else{
		for(int i=0; i<t->numTuples(); i++){
			if(cond[1] == "=="){
				if(firstOperandIsRelationName){
					if(t->getValue(i,attrNum) == cond[2]){
						tempTuple = t->getTuple(i);
						tempTable.addTuple(tempTuple);
					}
				}
				else{
					if(t->getValue(i,attrNum) == cond[0]){
						tempTuple = t->getTuple(i);
						tempTable.addTuple(tempTuple);
					}
				}
			}
			else if(cond[1] == "!="){
				if(firstOperandIsRelationName){
					if(t->getValue(i,attrNum) != cond[2]){
						tempTuple = t->getTuple(i);
						tempTable.addTuple(tempTuple);
					}
				}
				else{
					if(t->getValue(i,attrNum) != cond[0]){
						tempTuple = t->getTuple(i);
						tempTable.addTuple(tempTuple);
					}
				}
			}
			else if(cond[1] == "<"){
				if(t->getTypes()[attrNum] == INTEGER){
					if(firstOperandIsRelationName){
						temp1 = atoi((t->getValue(i,attrNum)).c_str());
						temp2 = atoi((cond[2]).c_str());
						if(temp1 < temp2){
							tempTuple = t->getTuple(i);
							tempTable.addTuple(tempTuple);
						}
					}
					else{
						temp1 = atoi((cond[0]).c_str());
						temp2 = atoi((t->getValue(i,attrNum)).c_str()); 
						if(temp1 < temp2){
							tempTuple = t->getTuple(i);
							tempTable.addTuple(tempTuple);
						}
					}
				}
				else{//not integer
					if(firstOperandIsRelationName){
						if(t->getValue(i,attrNum) < cond[2]){
							tempTuple = t->getTuple(i);
							tempTable.addTuple(tempTuple);
						}
					}
					else{
						if(t->getValue(i,attrNum) < cond[0]){
							tempTuple = t->getTuple(i);
							tempTable.addTuple(tempTuple);
						}
					}
				}
			}
			else if(cond[1] == ">"){
				if(t->getTypes()[attrNum] == INTEGER){
					if(firstOperandIsRelationName){
						temp1 = atoi((t->getValue(i,attrNum)).c_str());
						temp2 = atoi((cond[2]).c_str());
						if(temp1 > temp2){
							tempTuple = t->getTuple(i);
							tempTable.addTuple(tempTuple);
						}
					}
					else{
						temp1 = atoi((cond[0]).c_str());
						temp2 = atoi((t->getValue(i,attrNum)).c_str()); 
						if(temp1 > temp2){
							tempTuple = t->getTuple(i);
							tempTable.addTuple(tempTuple);
						}
					}
				}
				else{//not integer			
					if(firstOperandIsRelationName){
						if(t->getValue(i,attrNum) > cond[2]){
							tempTuple = t->getTuple(i);
							tempTable.addTuple(tempTuple);
						}
					}
					else{
						if(t->getValue(i,attrNum) > cond[0]){
							tempTuple = t->getTuple(i);
							tempTable.addTuple(tempTuple);
						}
					}
				}
			}
			else if(cond[1] == "<="){
				if(t->getTypes()[attrNum] == INTEGER){
					if(firstOperandIsRelationName){
						temp1 = atoi((t->getValue(i,attrNum)).c_str());
						temp2 = atoi((cond[2]).c_str());
						if(temp1 <= temp2){
							tempTuple = t->getTuple(i);
							tempTable.addTuple(tempTuple);
						}
					}
					else{
						temp1 = atoi((cond[0]).c_str());
						temp2 = atoi((t->getValue(i,attrNum)).c_str()); 
						if(temp1 <= temp2){
							tempTuple = t->getTuple(i);
							tempTable.addTuple(tempTuple);
						}
					}
				}
				else{//not integer			
					if(firstOperandIsRelationName){
						if(t->getValue(i,attrNum) <= cond[2]){
							tempTuple = t->getTuple(i);
							tempTable.addTuple(tempTuple);
						}
					}
					else{
						if(t->getValue(i,attrNum) <= cond[0]){
							tempTuple = t->getTuple(i);
							tempTable.addTuple(tempTuple);
						}
					}
				}
			}
			else if(cond[1] == ">="){
				if(t->getTypes()[attrNum] == INTEGER){
					if(firstOperandIsRelationName){
						temp1 = atoi((t->getValue(i,attrNum)).c_str());
						temp2 = atoi((cond[2]).c_str());
						if(temp1 >= temp2){
							tempTuple = t->getTuple(i);
							tempTable.addTuple(tempTuple);
						}
					}
					else{
						temp1 = atoi((cond[0]).c_str());
						temp2 = atoi((t->getValue(i,attrNum)).c_str()); 
						if(temp1 >= temp2){
							tempTuple = t->getTuple(i);
							tempTable.addTuple(tempTuple);
						}
					}
				}
				else{//not integer			
					if(firstOperandIsRelationName){
						if(t->getValue(i,attrNum) >= cond[2]){
							tempTuple = t->getTuple(i);
							tempTable.addTuple(tempTuple);
						}
					}
					else{
						if(t->getValue(i,attrNum) >= cond[0]){
							tempTuple = t->getTuple(i);
							tempTable.addTuple(tempTuple);
						}
					}
				}
			}
			else{
					error("select: Bad operator.");
			}
		}
	}
	return tempTable;
}

Table DBMS::project(Table *t, vector<string> attrs){
	//create temp table to be returned
	string tempName = "Projected " + t->getName();
	vector<string> tempAttrList;
	vector<int> tempTypes;
	vector<string> tempKey;
	tempKey.push_back(attrs[0]);
	for(int i=0; i<attrs.size(); i++){
		for(int j=0; j<t->getAttrList().size(); j++){
			if(attrs[i] == t->getAttrList()[j]){
				tempAttrList.push_back(attrs[i]);
				tempTypes.push_back(t->getTypes()[j]);
			}
		}
	}
	Table tempTable(tempName, tempAttrList, tempTypes, tempKey);
	
	//add tuples
	string tempValue;			//temp value to go in tuple
	vector<string> tempValues;	//temp tuple
	for(int i=0; i<t->numTuples(); i++){//tuple
		for(int j=0; j<attrs.size(); j++){//attr
			for(int k=0; k<t->getAttrList().size(); k++){//attrList
				if(attrs[j] == t->getAttrList()[k]){//user attr matches attrList
					tempValue = t->getValue(i,k);
					tempValues.push_back(tempValue);
				}
			}
		}
		tempTable.addTuple(tempValues);
		tempValues.clear();
	}
	return tempTable;
}

Table DBMS::crossProduct(Table *t1, Table *t2){
	vector<string> tempAttrList;
	string tempAttr;
	//set attribute list
	for(int i=0; i<t1->getAttrList().size(); i++){
		tempAttr = t1->getAttrList()[i];
		tempAttrList.push_back(tempAttr);
	}
	for(int i=0; i<t2->getAttrList().size(); i++){
		tempAttr = t2->getAttrList()[i];
		tempAttrList.push_back(tempAttr);
	}
	
	//set types
	vector<int> tempTypes;
	for(int i=0; i<t1->getTypes().size(); i++){
		tempTypes.push_back(t1->getTypes()[i]);
	}
	for(int i=0; i<t2->getTypes().size(); i++){
		tempTypes.push_back(t2->getTypes()[i]);
	}
	
	//set keys
	string tempKey;
	vector<string> tempKeyList;
	for(int i=0; i<t1->getKey().size(); i++){
		tempKey = t1->getKey()[i];
		tempKeyList.push_back(tempKey);
	}
	for(int i=0; i<t2->getKey().size(); i++){
		tempKey = t2->getKey()[i];
		tempKeyList.push_back(tempKey);
	}
	
	//create table
	string tempName = t1->getName() + " X " + t2->getName();
	Table tempTable = Table(tempName, tempAttrList, tempTypes, tempKeyList);
	
	//add tuples
	vector<string> tempValues;
	string tempValue;
	for(int i=0; i<t1->numTuples(); i++){
		for(int j=0; j<t2->numTuples(); j++){
			for(int k=0; k<t1->getAttrList().size(); k++){//table 1 attrList
				tempValue = t1->getValue(i, k);
				tempValues.push_back(tempValue);
			}
			for(int k=0; k<t2->getAttrList().size(); k++){//table 2 attrList
				tempValue = t2->getValue(j, k);
				tempValues.push_back(tempValue);
			}
			tempTable.addTuple(tempValues);
			tempValues.clear();
		}
	}
	return tempTable;
}

//Add Table to tables vector
void DBMS::createTable(string name_, vector<string> attrList_, vector<int> types_, vector<string> key_){
	Table *t = new Table(name_, attrList_, types_, key_);
	tables.push_back(t);
}

void DBMS::addTable(Table t){
	Table *tempTable = new Table(t);
	tables.push_back(tempTable);
}

//Deletes a tuple (row) from the table
void DBMS::deleteTuple(Table *t, vector<string> deleteKey){
	t->deleteTuple(deleteKey);
}

void DBMS::updateAttr(Table *t, vector<string> key, string attrOld, string attrNew){
	if(integerCheck(attrOld)){
		if(!integerCheck(attrNew)){
			error("updateAttr: New attribute must be integer.");
		}
	}
	t->updateAttribute(key, attrOld, attrNew);
}

void DBMS::addTuple(Table *t, vector<string> tuple){
	t->addTuple(tuple);
}

bool DBMS::isTable(string tableName){
	for(int i=0; i<tables.size(); i++){
		if(tableName == tables[i]->getName()){
			return true;
		}
	}
	return false;
}

void DBMS::open(string fileName){
	dbFile.open(fileName.c_str());
}

void DBMS::write(Table *t){
	string fileName = t->getName();
	fileName += ".db";
	ofstream writeOut;
	if(dbFile.is_open()){
		dbFile.close();
	}
	writeOut.open(fileName.c_str());
	/* 
	This function writes a table to file in the following format:
	
	RelationName (string)
	numTuples (int)
	numAttr (int)
	numKeys (int)
	attr0, attr1, attr2.... (vector<string>)
	INTEGER or STRING, INTEGER or STRING (types, INTEGER = 0, STR = 1) (vector<int>)
	keyList (vector<string>)
	
	values[0][0],values[0][1],...
	values[1][0],values[1][1],...
	*/
	writeOut << t->getName() << "\n";
	writeOut << t->numTuples() << "\n";
	writeOut << t->numAttr() << "\n";
	writeOut << t->numOfKeys() << "\n";
	for(int i=0; i<t->getAttrList().size(); i++){
		writeOut << t->getAttrList()[i] << ",";
	}
	writeOut << "\n";
	for(int i=0; i<t->getTypes().size(); i++){
		writeOut << t->getTypes()[i] << ",";
	}
	writeOut << "\n";
	for(int i=0; i<t->getKey().size(); i++){
		writeOut << t->getKey()[i] << ",";
	}
	writeOut << "\n";
	//Writing out actual table
	for(int i=0; i<t->numTuples(); i++){
		for(int j=0; j<t->getAttrList().size(); j++){
			writeOut << t->getValue(i,t->getAttrList()[j]) << ",";
		}
		writeOut << "\n";
	}
	
	writeOut << "\n\n";
	writeOut.close();
	cout << "File " << fileName << " successfully written..." << endl;
	
}

void DBMS::read(string fileName){
	if(dbFile.is_open()){
		dbFile.close();
	}
	dbFile.open(fileName.c_str());
	// reads in a table with the format detailed in DBMS::write()
	string tableName;
	getline(dbFile, tableName);
	//cout << "Creating table " << tableName << endl;
	string numberTuples, numberAttr, numberKeys;
	getline(dbFile, numberTuples);
	getline(dbFile, numberAttr);
	getline(dbFile, numberKeys);
	if(/*numberTuples == "0" ||*/ numberAttr == "0" || numberKeys == "0" ||
		numberTuples == "" || numberAttr == "" || numberKeys == ""){
		error("numAttr, and numKeys must contain a non-zero integer");
	}
	string newAttrs, newTypes, newKey;
	getline(dbFile, newAttrs);
	getline(dbFile, newTypes);
	getline(dbFile, newKey);
	// We will have to split up these strings and toss them into their vectors:	
	vector<string> newAttrList;
	vector<int> newTypesList;
	vector<string> newKeyList;
	
	stringstream sstr(newAttrs); //splitting up attribute string
	string tempAttr;
	while(getline(sstr, tempAttr, ',')){
		newAttrList.push_back(tempAttr);
		//cout << "tempAttr pushed" << endl;
	}
	
	stringstream sstr1(newTypes); //splitting up type string
	string tempType;
	while(getline(sstr1, tempType, ',')){
		int tempTypeInt = atoi(tempType.c_str());
		newTypesList.push_back(tempTypeInt);
		//cout << "tempType pushed" << endl;
	}
	
	stringstream sstr2(newKey); //splitting up key string
	string tempKey;
	while(getline(sstr2, tempKey, ',')){
		newKeyList.push_back(tempKey);
		//cout << "tempKey pushed" << endl;
	}
	
	//Calling Table constructor
	createTable(tableName, newAttrList, newTypesList, newKeyList);
	
	int tupleInts = atoi(numberTuples.c_str());
	//Adding new tuples
	for(int i=0; i<tupleInts; i++){
		string tupleRow;
		getline(dbFile, tupleRow); //reads in tuple from file
		vector<string> newTuple;
		string str;
		stringstream tupleStr(tupleRow);
		while(getline(tupleStr, str, ',')){  //separates string at comma
			newTuple.push_back(str);
		}
		
		addTuple(getTable(tableName), newTuple); //add tuple to the table
		newTuple.clear(); //empty vector for next go-around
		//cout << "new tuple pushed and cleared" << endl;
	}
	
	dbFile.close();	
}

void DBMS::close(){
	if(dbFile.is_open()){
		dbFile.close();
		cout << "File closed." << endl;
	}
	else
		cout << "No file open!" << endl;
}

void DBMS::show(Table *t){
	t->print();
}

void DBMS::exit(){
	cout << "Exiting...\n";
	//nop
}

/*
Query takes in a vector of strings and returns a table based on the tokens.
We start with the last token and push the token on the stack if it is not
";", "(", ")", "select", "*", or "project". When one of these keywords are found,
it is handled accordingly.
*/
Table DBMS::Query(vector<string> tokens){
	//return the table if the query only contains a relation name
	if(tokens.size() <= 2){
		Table *t = getTable(tokens[0]);
		return *t;
	}
	stack<string> opStack;	//stack used to keep track of operations and operands
	string table1, table2;	//table names used in operations
	for(int i=tokens.size()-1; i>=0; i--){	//iterate through the tokens vector, starting from the end
		if(tokens[i] == ";" || tokens[i] == "(" || tokens[i] == ")"){
			;//nop
		}
		else if(tokens[i] == "*"){
			//get one more token from the vector
			//pop table name from stack
			//do cross product
			//push new table on stack
			table2 = opStack.top();
			opStack.pop();
			i--;
			table1 = tokens[i];
			Table tempTable = crossProduct(getTable(table1),getTable(table2));
			addTable(tempTable);
			opStack.push(tempTable.getName());
		}
		else if(tokens[i] == "select"){
			//pop conditions from stack
			//pop table name from stack
			//do select
			//push new table on stack
			vector<string> cond(3);
			cond[0] = opStack.top();
			opStack.pop();
			cond[1] = opStack.top();
			opStack.pop();
			cond[2] = opStack.top();
			opStack.pop();
			table1 = opStack.top();
			opStack.pop();
			Table tempTable = select(getTable(table1), cond);
			addTable(tempTable);
			opStack.push(tempTable.getName());
		}
		else if(tokens[i] == "project"){
			//pop attribute list from stack
			//pop table name from stack
			//do project
			//push new table on stack
			vector<string> tempAttrList;
			while(!isTable(opStack.top())){//if next element on stack is not a table
				tempAttrList.push_back(opStack.top());
				opStack.pop();
			}
			table1 = opStack.top();
			opStack.pop();
			Table tempTable = project(getTable(table1), tempAttrList);
			addTable(tempTable);
			opStack.push(tempTable.getName());
		}
		else{//not a keyword
			opStack.push(tokens[i]);
		}
	}
	if(opStack.size() != 1){//should only have one table name on the stack
		error("Query: something went wrong!");
	}
	else{
		Table *t = getTable(opStack.top());
		return *t;
	}
}

#endif
